package com.hongyun.tms.infra.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.hongyun.tms.common.constants.CustomerTypeConstant;
import com.hongyun.tms.common.constants.DefaultCreateByConstant;
import com.hongyun.tms.common.dto.mcustomer.MCustomerImportDto;
import com.hongyun.tms.common.enums.BizCode;
import com.hongyun.tms.entity.MCustomer;
import com.hongyun.tms.exception.BusinessRuntimeException;
import com.hongyun.tms.service.IMCustomerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 客户信息导入
 *
 * @author liPeng
 * @date 2024-09-10
 */
@Slf4j
@Component
public class MCustomerExcelListener extends DefaultExcelListener<MCustomerImportDto> {

    @Autowired
    private IMCustomerService customerService;

    public MCustomerExcelListener(IMCustomerService customerService) {
        this.customerService = customerService;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("成功读取excel中【" + getRows().size() + "】条客户数据");

        // 提取身份证号和纳税人识别号，使用Set避免重复
        Set<String> idNumberSet = new HashSet<>();
        Set<String> tinSet = new HashSet<>();
        Set<String> duplicateIdNumbers = new HashSet<>();
        Set<String> duplicateTins = new HashSet<>();

        // 校验Excel中的数据是否合法（个人只能填身份证，企业只能填纳税号）
        validateExcelData(getRows());




        // 根据客户类型收集身份证号和纳税号
        for (MCustomerImportDto customer : getRows()) {
            customer.convertTypeStrToType();
            if (CustomerTypeConstant.PERSON.equals(customer.getType())) { // 个人
                if (!idNumberSet.add(customer.getIdNumber())) {
                    duplicateIdNumbers.add(customer.getIdNumber()); // 发现重复身份证号
                }
            } else if (CustomerTypeConstant.ENTERPRISE.equals(customer.getType())) { // 企业
                if (!tinSet.add(customer.getTin())) {
                    duplicateTins.add(customer.getTin()); // 发现重复纳税号
                }
            }
        }

        // 如果发现 Excel 中有重复的身份证号或纳税号，抛出异常
        if (CollectionUtils.isNotEmpty(duplicateIdNumbers) || CollectionUtils.isNotEmpty(duplicateTins)) {
            StringBuilder excelDuplicateMessage = new StringBuilder("Excel 中发现重复的数据: ");
            if (CollectionUtils.isNotEmpty(duplicateIdNumbers)) {
                excelDuplicateMessage.append("身份证号: ").append(duplicateIdNumbers).append("; ");
            }
            if (CollectionUtils.isNotEmpty(duplicateTins)) {
                excelDuplicateMessage.append("纳税人识别号: ").append(duplicateTins).append("; ");
            }
            throw new BusinessRuntimeException(BizCode.BIZ_ERROR, excelDuplicateMessage.toString());
        }

        // 校验数据库中的唯一性
        checkUniqueInDatabase(idNumberSet, tinSet);

        // 保存客户数据到数据库
        saveCustomers();
        log.info("成功插入excel中【" + getRows().size() + "】条客户数据");
    }


    /**
     * 校验Excel中客户数据是否合法：个人只能填写身份证号，企业只能填写纳税号
     */
    private void validateExcelData(List<MCustomerImportDto> customers) {
        for (int i = 0; i < customers.size(); i++) {
            MCustomerImportDto customer = customers.get(i);
            customer.convertTypeStrToType();
            int rowIndex = i + 1; // 行号从1开始计数
            if (CustomerTypeConstant.PERSON.equals(customer.getType())) { // 个人
                if (StringUtils.isBlank(customer.getIdNumber())) {
                    throw new BusinessRuntimeException(BizCode.BIZ_ERROR,
                            String.format("第%d行：个人客户必须填写身份证号！", rowIndex + 1));
                }
                if (StringUtils.isNotBlank(customer.getTin())) {
                    throw new BusinessRuntimeException(BizCode.BIZ_ERROR,
                            String.format("第%d行：个人客户不能填写纳税人识别号！", rowIndex + 1));
                }
            } else if (CustomerTypeConstant.ENTERPRISE.equals(customer.getType())) { // 企业
                if (StringUtils.isBlank(customer.getTin())) {
                    throw new BusinessRuntimeException(BizCode.BIZ_ERROR,
                            String.format("第%d行：企业客户必须填写纳税人识别号！", rowIndex + 1));
                }
                if (StringUtils.isNotBlank(customer.getIdNumber())) {
                    throw new BusinessRuntimeException(BizCode.BIZ_ERROR,
                            String.format("第%d行：企业客户不能填写身份证号！", rowIndex + 1));
                }
            }
        }
    }


    /**
     * 校验数据库中的身份证号和纳税人识别号的唯一性
     *
     * @param idNumbers 身份证号集合
     * @param tins      纳税人识别号集合
     */
    private void checkUniqueInDatabase(Set<String> idNumbers, Set<String> tins) {
        // 使用 Set 存储已提示过的身份证号和纳税人识别号，避免重复提示
        Set<String> duplicateIdNumbers = new HashSet<>();
        Set<String> duplicateTins = new HashSet<>();

        // 合并查询身份证号和纳税号，减少数据库查询次数
        List<MCustomer> existingCustomers = customerService.list(new LambdaQueryWrapper<MCustomer>()
                .in(!idNumbers.isEmpty(), MCustomer::getIdNumber, idNumbers)
                .or()
                .in(!tins.isEmpty(), MCustomer::getTin, tins));

        if (!existingCustomers.isEmpty()) {
            StringBuilder dbDuplicateMessage = new StringBuilder("以下数据已存在于数据库: ");
            for (MCustomer customer : existingCustomers) {
                // 只提示一次重复的身份证号
                if (idNumbers.contains(customer.getIdNumber()) && duplicateIdNumbers.add(customer.getIdNumber())) {
                    dbDuplicateMessage.append("身份证号: ").append(customer.getIdNumber()).append("; ");
                }
                // 只提示一次重复的纳税号
                if (tins.contains(customer.getTin()) && duplicateTins.add(customer.getTin())) {
                    dbDuplicateMessage.append("纳税人识别号: ").append(customer.getTin()).append("; ");
                }
            }
            throw new BusinessRuntimeException(BizCode.BIZ_ERROR, dbDuplicateMessage.toString());
        }
    }


    /**
     * 保存客户数据到数据库
     */
    private void saveCustomers() {
        // 处理读取的数据并插入数据库
        List<MCustomer> insertList = Lists.newArrayList();
        for (MCustomerImportDto customer : getRows()) {
            MCustomer insertTarget = new MCustomer()
                    .setType(customer.getType())
                    .setCustomerName(customer.getCustomerName())
                    .setTin(customer.getTin())
                    .setIdNumber(customer.getIdNumber())
                    .setContacts(customer.getContacts())
                    .setTel(customer.getTel())
                    .setAddress(customer.getAddress())
                    .setCreateBy(DefaultCreateByConstant.userId)
                    .setUpdateBy(DefaultCreateByConstant.userId)
                    .setCreateByName(DefaultCreateByConstant.userId.toString())
                    .setUpdateByName(DefaultCreateByConstant.userId.toString())
                    .setCreateTime(LocalDateTime.now())
                    .setUpdateTime(LocalDateTime.now());

            insertList.add(insertTarget);
        }

        List<List<MCustomer>> partitionList = Lists.partition(insertList, getSplitThreshold());
        for (List<MCustomer> customerList : partitionList) {
            customerService.saveBatch(customerList);
        }
    }


}
